

#include <iostream>
#include <unistd.h>	
#include <vector>
#include <fstream>
#include <sstream>


#include "uRISC_Beta.h"
#include "ULA.h"


using namespace std;

typedef unsigned short int u16bits;
typedef unsigned char u8bits;


int charToHex(char _ch){
	switch(_ch){
		case '0': return 0;
		case '1': return 1;
		case '2': return 2;
		case '3': return 3;
		case '4': return 4;
		case '5': return 5;
		case '6': return 6;
		case '7': return 7;
		case '8': return 8;
		case '9': return 9;
		case 'a': return 10;
		case 'b': return 11;
		case 'c': return 12;
		case 'd': return 13;
		case 'e': return 14;
		case 'f': return 15;
		default: return 0;
	}
}

u16bits strToU16(string _inStr){
	if(_inStr.size() > 4) return 0;
	u16bits temp = 0;
	u16bits base = 0x1000;
	for(unsigned int i=0; i < _inStr.size(); i++){
		temp += charToHex(_inStr[i])*base;
		base = (base >> 4);
	}
	return temp;	
}

u16bits estende8(unsigned char _inbits){ // estede sinal
	u16bits temp = (_inbits & 0x80) ? 0xFF00 : 0x0000; 
	return temp + _inbits;
}

u16bits estende12(u16bits _inbits){ // estede sinal
	u16bits temp = (_inbits & 0x0800) ? 0xF000 : 0x0000; 
	return temp + _inbits;
}

u16bits estende11(u16bits _inbits){ // estede sinal
	u16bits temp = (_inbits & 0x0400) ? 0xF800 : 0x0000; 
	return temp + _inbits;
}



//class RegFile{
//public:
//	RegFile();
//	~RegFile();
//	void enable(u16bits r_addr_1, u16bits r_addr_2, u16bits &r_data_1, u16bits &r_data_2);                                  // R
//	void enable(u16bits r_addr_1, u16bits r_addr_2, u16bits w_addr, u16bits &r_data_1, u16bits &r_data_2, u16bits w_data);  // RW
//	void enable(u16bits w_addr, u16bits w_data);                                                                            // W
//private:
//	u16bits *_regFie;	
//};


RegFile::RegFile(){
try{
	_regFie = new u16bits [8]; // aloca dinamicamente
	for(int i = 0; i<8; i++) _regFie[i] = 0x0000; //inicia com zeros
	
}catch(...){
	// tratar erro em alocacao
	cout << "Erro na inicializacao de RegFile" << endl;
	exit(1);
}}

RegFile::~RegFile(){
	delete [] _regFie;  //desaloca mem;
}

void RegFile::enable(u16bits r_addr_1, u16bits r_addr_2, u16bits &r_data_1, u16bits &r_data_2){
	r_data_1 = _regFie[r_addr_1];   // leitura porta 1
	r_data_2 = _regFie[r_addr_2];   // leitura porta 2
}

void RegFile::enable(u16bits r_addr_1, u16bits r_addr_2, u16bits w_addr, u16bits &r_data_1, u16bits &r_data_2, u16bits w_data){
	r_data_1 = _regFie[r_addr_1];   // leitura porta 1
	r_data_2 = _regFie[r_addr_2];   // leitura porta 2
	_regFie[w_addr] = w_data;       // grava dado
}

void RegFile::enable(u16bits w_addr, u16bits w_data){
	_regFie[w_addr] = w_data;       // grava dado
}


//class MemRAM{
//public:
//	MemRAM();
//	~MemRAM();
//	void enableRead(u16bits addr, u16bits &memData);
//	void enableWrite(u16bits addr, u16bits memData);	
//	void loadProg(void);
//private:
//	u16bits *_memRAM;	
//};

MemRAM::MemRAM(){
	try{
		_memRAM = new u16bits [65536]; // aloca dinamicamente
		for(int i = 0; i<65536; i++) _memRAM[i] = 0x0000; //inicia com zeros
		
	}catch(...){
		// tratar erro em alocacao
		cout << "Erro na inicializacao de MemRAM" << endl;
		exit(1);
	}
}

MemRAM::~MemRAM(){
	delete [] _memRAM;  //desaloca mem;
}

void MemRAM::enableRead(u16bits addr, u16bits &memData){
	memData = _memRAM[addr];
}

void MemRAM::enableWrite(u16bits addr, u16bits memData){
	_memRAM[addr] = memData;
}

void MemRAM::loadProg(ifstream &inFile){
	//Carga do programa na memoria RAM
	if( inFile == NULL ){
		cout << "Erro ao carregar arquivo na RAM" << endl;
		exit(1);
	}
	string inStr;
	u16bits memPos = 0;
	while(!inFile.eof()){
		inFile >> inStr;
		if(inStr == "address"){
			inFile >> inStr; // pega endereco
			memPos = strToU16(inStr);
			continue;
		}
		enableWrite(memPos, strToU16(inStr));
		memPos++;		
	}	
}


inline u16bits get_OP5(u16bits rIR){ return (rIR & 0x07C0) >> 6; }
inline u16bits get_OP2(u16bits rIR){ return (rIR & 0x3000) >> 12; }
inline u16bits get_WC(u16bits rIR){ return (rIR & 0x3800) >> 11; }
inline u16bits get_RA(u16bits rIR){ return (rIR & 0x0038) >> 3; }
inline u16bits get_RB(u16bits rIR){ return (rIR & 0x0007); }
inline u16bits get_Offset11(u16bits rIR){ return (rIR & 0x07FF); }
inline u16bits get_Offset8(u16bits rIR){ return (rIR & 0x00FF); }
inline u16bits get_Offset12(u16bits rIR){ return rIR & 0x0FFF; }
inline u16bits get_Cond(u16bits rIR){ return (rIR & 0x0F00) >> 8; }
inline u16bits get_R(u16bits rIR){ return (rIR & 0x0400) >> 10; }
inline u16bits get_Formato(u16bits rIR){ return (rIR & 0xC000) >> 14; }


//class uRISC_Beta{
//public:
//	uRISC_Beta();
//	void ciclo_IF(void);
//	void ciclo_ID(void);
//	void ciclo_ExMem(void);
//	void ciclo_WB(void);
//private:	
//	//Data Path
//	RegFile regFile;   //Arquivo de registradores
//	MemRAM  memRAM;    //Memoria de programa e dados
//	u16bits regA;   //registrador de entrada A da ALU
//	u16bits regB;   //registrador de entrada B da ALU
//	u16bits regIR;  //registrador de Instrucao
//	u16bits regMR;  //registrador de Memoria de Dado da memoria
//	u16bits regALU; //registrador de saida da ALU
//	u16bits regPC;  //registrador PC
//	u16bits regFlags; //registrador de Flags
//	//sinais de controle
//	enum _execution_code { F1, F2, F3, F4_0, F4_1, F5, F6_0, F6_1, F7_0, F7_1 } execution_code;
//};


uRISC_Beta::uRISC_Beta(ifstream &inFile){
	memRAM.loadProg(inFile);
	regA = 0;
	regB = 0;
	regIR = 0;
	regMR = 0;
	regALU = 0;
	regPC = 0;
	regFlags = 0;
	viewOpALU = 0;
}

void uRISC_Beta::ciclo_IF(){
	//--------------------------------------------------------------------------------------------------
	//IF
	/*  (instruction fetch): a próxima instrução a ser executada é buscada na memória
	 *                       e armazenada no registrador de instruções (IR);
	 */
	// IR = Memory[PC]
	// PC = PC + 1
	memRAM.enableRead(regPC,regIR);
	regPC = regPC + 1;	
}

void uRISC_Beta::ciclo_ID(){
	//--------------------------------------------------------------------------------------------------
	//ID
	/* (instruction decode): a instrução sendo executada é decodificada e os operandos
	 *                       são buscados no banco de registradores;
	 */
	// A = reg[IR->RA]
	// B = reg[IR->RB]
	// ALUout = PC + (signed-extend[desloca])
	
//	//se HALT para o programa do simulador
//	if(rIR == 0x2FFF) break;

	switch(get_Formato(regIR)){
	case 0:  //formato 4,5,6
			if(get_OP2(regIR) == 0) execution_code = F4_0;       // OP = 0
			else if(get_OP2(regIR) == 1) execution_code = F4_1;  // OP = 1
			else if(get_OP2(regIR) == 2) execution_code = F5;    // OP = 2
			else {                                              // OP = 3
				if(get_R(regIR) == 0) execution_code = F6_0;
				else                  execution_code = F6_1;
			}
			//busca
			if(execution_code == F4_0 || execution_code == F4_1) 
				regA = estende8(get_Offset8(regIR));                         // busca operando para F4_0 e F4_1			
			if(execution_code == F5) 
				regA = estende12(get_Offset12(regIR));                       // busca operando para F5
			if(execution_code == F6_0 || execution_code == F6_1) 
				regFile.enable(get_RB(regIR), get_RB(regIR), regA, regB );  // busca operando para F6_0 e F6_1
		break;		
		
	case 1:  //formato 1,7
			if(get_OP5(regIR) == 0x0014) execution_code = F7_0;      //load,  OP = 10100(b)
			else if(get_OP5(regIR) == 0x0016) execution_code = F7_1; //store, OP = 10110(b)
			else execution_code = F1;                               // ALU
			//busca
			regFile.enable(get_RA(regIR), get_RB(regIR), regA, regB);  //busca operandos para F1 e F7
		break;
		
	case 2:  //formato 2
			execution_code = F2; 
			//busca
			regA = estende11(get_Offset11(regIR));
		break;
		
	case 3:  //formato 3
			execution_code = F3;
			regFile.enable(get_WC(regIR), get_WC(regIR), regA, regB);
			if(get_R(regIR) == 0){
				regA = get_Offset8(regIR);       //low
				regB = (regB & 0xFF00);
			}
			else{
				regA = get_Offset8(regIR) << 8;  //hi
				regB = (regB & 0x00FF);
			}
		break;
		
	default: // erro
		cout << "Erro ao decodificar formato. Passo ID" << endl;
		exit(1);
	}	
	
	// Teste
//	cout << "Formato: " << get_Formato(regIR) << endl;
//	cout << "OP2: " << get_OP2(regIR) << endl;
//	cout << "Execution code: " << execution_code << endl;
}

void uRISC_Beta::ciclo_ExMem(){
	//--------------------------------------------------------------------------------------------------
	//EX/MEM
	/* (execute and memory): a instrução é executada e as condições dos resultados são "setados"
	 *                      (condition codes) para operações de ALU. Loads, stores, o cálculo
	 *                       do endereço efetivo e a resolução de branches são feitos também 
	 *                       nesse ciclo;
	 */
	bool condDesvio;
	switch(get_Cond(regIR)){
		case 4: condDesvio = (bool) (regFlags & 0x20); break; // Resultado ALU negativo
		case 5: condDesvio = (bool) (regFlags & 0x10); break; // Resultado ALU zero
		case 6: condDesvio = (bool) (regFlags & 0x04); break; // Carry da ALU 
		case 7: condDesvio = (bool) (regFlags & 0x08); break; // Resultado ALU negativo ou zero
		case 0: condDesvio = (bool) (regFlags & 0x01); break; // Resultado ALU diferente de zero
		case 3: condDesvio = (bool) (regFlags & 0x02); break; // Resultado ALU overflow
	}
	
	Flags *_flags = new Flags;
	
	switch(execution_code){
	case F1:  // ALU
		// colocar ALU aqui.
		// alu( regA, regB, regALU, regFlags );
		//ULA::ExecutaOperacao(operando, teste_a, teste_b, teste_c, _flags);
		ULA::ExecutaOperacao(get_OP5(regIR), regA, regB, regALU, _flags);
		//muda flags
		regFlags = 0;
		regFlags += (_flags->getFlag(neg)) ? 0x20 : 0x00;
		regFlags += (_flags->getFlag(zero)) ? 0x10 : 0x00;
		regFlags += (_flags->getFlag(negzero)) ? 0x08 : 0x00;
		regFlags += (_flags->getFlag(carry)) ? 0x04 : 0x00;
		regFlags += (_flags->getFlag(overflow)) ? 0x02 : 0x00;
		regFlags += (_flags->getFlag(vero)) ? 0x01 : 0x00;
		
		viewOpALU = get_OP5(regIR);	//para Interface Grafica
		break;
	case F2:
		//Usa a ALU para: regALU = regA sem  setar flags
		ULA::ExecutaOperacao( 0x09, regA, regB, regALU, _flags);
		viewOpALU = 0x09; //para Interface Grafica
		break;
	case F3:  
		//Usa a ALU para: regALU = regA or regB sem setar flags
		ULA::ExecutaOperacao( 0x04, regA, regB, regALU, _flags);
		viewOpALU = 0x04; //para Interface Grafica
		break;
	case F4_0: // Jump False: jf.cond Destino 
		//Usa a ALU para: regALU = regA + regPC sem setar flags 
		if(!condDesvio) ULA::ExecutaOperacao( 0x18, regA, regPC, regALU, _flags);
		else            ULA::ExecutaOperacao( 0x09, regPC, regPC, regALU, _flags);
		viewOpALU = (!condDesvio) ? 0x18 : 0x09; //para Interface Grafica
		break;
	case F4_1: // Jump True: jf.cond Destino
		//Usa a ALU para: regALU = regA + regPC sem setar flags 
		if(condDesvio) ULA::ExecutaOperacao( 0x18, regA, regPC, regALU, _flags);
		else           ULA::ExecutaOperacao( 0x09, regPC, regPC, regALU, _flags);
		viewOpALU = (condDesvio) ? 0x18 : 0x09; //para Interface Grafica
		break;
	case F5:  
		//Usa a ALU para: regALU = regA + regPC sem  setar flags
		ULA::ExecutaOperacao( 0x18, regA, regPC, regALU, _flags);
		viewOpALU = 0x18; //para Interface Grafica
		break;
	case F6_0:
	case F6_1:
		//Usa a ALU para: regALU = regB sem  setar flags
		ULA::ExecutaOperacao( 0x09, regB, regB, regALU, _flags);
		viewOpALU = 0x09; //para Interface Grafica
		break;
	case F7_0: //C = Mem[A] 	load c, a
		memRAM.enableRead(regA, regMR); //le gravando em regMR
		break;
	case F7_1: //Mem[A] = B	     store a, b
		memRAM.enableWrite(regA, regB);
		break;;
	default: // erro
		cout << "Erro em passo EX/MEM" << endl;
		exit(1);
	}
	
	delete _flags;
}

void uRISC_Beta::ciclo_WB(){
	//--------------------------------------------------------------------------------------------------
	//WB
	/* (write back): os resultados são escritos no banco de registradores.
	 */
	switch(execution_code){
	case F1: 
	case F2: 
	case F3:
		regFile.enable(get_WC(regIR), regALU); // grava no arquivo de registradores para F1, F2 e F3
		break;
	case F4_0:   
	case F4_1:  
		regPC = regALU;  // Para F4_0 e F4_1
		break;
	case F5:  
		regPC = regALU;  // Para F5
		break;
	case F6_0:
		regFile.enable(7, regPC); // copia PC para R7
	case F6_1:
		regPC = regALU;  // Para F6_0 e F6_1      
		break;
	case F7_0: 
		regFile.enable(get_WC(regIR), regMR);	//grava regMR em lugar apontado por IR->WC
		break;
	case F7_1: break; //nao faz nada aqui
	
	default: // erro
		cout << "Erro em passo WB" << endl;
		exit(1);
	}	
}

u16bits uRISC_Beta::getRegFile(u8bits reg){
	u16bits var_temp;
	regFile.enable(reg, reg, var_temp, var_temp);
	return var_temp;
}
u16bits uRISC_Beta::getMemRAM(u16bits addr){
	u16bits temp;
	memRAM.enableRead(addr, temp);
	return temp;
}

